#include "precompiled.h"
#include "Sound_Manager.h"

//...................................................
#include "tbb/concurrent_hash_map.h"
#include "tbb/concurrent_queue.h"
using namespace tbb;
//...................................................


//...................................................
const std::string SOUNDPATH("../sounds/");
//...................................................
// Load up some debug sounds for testing.
void debug_loadsounds(){

	// Create paths and keys.
	std::vector<std::string> paths;
	paths.push_back( SOUNDPATH + "/test/wave.mp3" );
	paths.push_back( SOUNDPATH + "/0/mt_engine_bg_0.wav" );
	paths.push_back( SOUNDPATH + "/0/jump.wav" );
	paths.push_back( SOUNDPATH + "/0/bounce.wav" );
	paths.push_back( SOUNDPATH + "/0/shot_fired.wav" );
	paths.push_back( SOUNDPATH + "/0/shot_landed.wav" );

	std::vector<std::string> keys;
	keys.push_back( "wave" );
	keys.push_back( "mt_engine_bg_0" );
	keys.push_back( "jump" );
	keys.push_back( "bounce" );
	keys.push_back( "shot_fired" );
	keys.push_back( "shot_landed" );

	SoundManager::LoadSounds( paths, keys );

	// Place an order to play the sound.
	SoundManager::OrderASound( "mt_engine_bg_0", true );
}



void ERRCHECK(FMOD_RESULT result)
{
	if (result != FMOD_OK)
	{
		printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
		exit(-1);
	}
}

namespace SoundManager{

	FMOD::System     *system;
	FMOD::Channel    *channel = 0;
	FMOD_RESULT       result;
	unsigned int      version;

	// A map of sounds and their key names.
	concurrent_hash_map<std::string, FMOD::Sound*> sound_map;
	typedef concurrent_hash_map<std::string, FMOD::Sound*>::accessor sound_map_accessor;
	typedef concurrent_hash_map<std::string, FMOD::Sound*>::const_accessor sound_map_constant_accessor;

	typedef struct{
		std::string key;
		bool loop;
	} sound_order;

	// An asynchronous Q of orders to play a sound.
	concurrent_queue<sound_order> sound_orders;


	void SoundManager::Init(){
		result = FMOD::System_Create( &system );
		ERRCHECK( result );

		result = system->getVersion(&version);
		ERRCHECK(result);

		if (version < FMOD_VERSION)
		{
			printf("Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", version, FMOD_VERSION);
			return;
		}

		result = system->init(64, FMOD_INIT_NORMAL, 0);
		ERRCHECK(result);
	}

	void SoundManager::LoadSounds( std::vector<std::string> paths,
		std::vector<std::string> keys ){
			sound_map_accessor a;

			// Make sure for ever path value we have a key.
			if( paths.size() != keys.size() ){
				printf("SOUND ERROR: loading unequal number of paths and keys...\n");
				return;
			}

			// Add our sound pairs.
			for( int c = 0; c < paths.size(); c++ ){

				FMOD::Sound *s;
				result = system->createSound( paths.at( c ).c_str(), 
					FMOD_DEFAULT, 0, &s);
				ERRCHECK(result);

				result = s->setMode(FMOD_LOOP_NORMAL);
				ERRCHECK(result);

				// Map the sound and key.
				sound_map.insert( a, keys.at( c ) );
				a->second = s;

				printf("loaded sound [%s] as [%s]\n",
					paths.at( c ).c_str(), keys.at( c ).c_str() );
			}
	}

	// Place an order to play a sound.
	void SoundManager::OrderASound( std::string key, bool loop ){
		sound_order o = { key, loop };
		sound_orders.push( o );
	}

	void SoundManager::Destroy(){
		
		// Release all of our sounds.
		for( auto it = sound_map.begin(); it != sound_map.end(); it++ ){
			result = it->second->release();
			ERRCHECK(result);
		}

		printf("Successfully released all sounds...\n");
	}

	void SoundManager::Run(){

		Init();

		debug_loadsounds();

		bool running = true;
		while( running ){
			
			sound_map_constant_accessor ca;

			// Play all of our sound orders.
			while( !sound_orders.empty() ){
				
				sound_order item;
				if( sound_orders.try_pop( item ) ){

					// Quit if told to.
					if( item.key == "quit" )
						running=false;
					
					// Find the sound.
					if( sound_map.find( ca, item.key ) ){

						// Loop the sound?
						if( item.loop ){
							result = ca->second->setMode(FMOD_LOOP_NORMAL);
							ERRCHECK(result);
						}
						else{
							result = ca->second->setMode(FMOD_LOOP_OFF);
							ERRCHECK(result);
						}
						
						// Play the sound.
						result = system->playSound(
							FMOD_CHANNEL_FREE,
							ca->second, 
							false, 
							&channel);
						ERRCHECK(result);
					}
				}
			}

			system->update();

		}

		Destroy();
	}
};
